/**
 * Count page views form GA or local cache file.
 *
 * Dependencies:
 *   - jQuery
 *   - countUp.js <https://github.com/inorganik/countUp.js>
 */

const getInitStatus = (function () {
    let hasInit = false;
    return () => {
        let ret = hasInit;
        if (!hasInit) {
            hasInit = true;
        }
        return ret;
    };
}());

const PvOpts = (function () {
    function getContent(selector) {
        return $(selector).attr("content");
    }

    function hasContent(selector) {
        let content = getContent(selector);
        return (typeof content !== "undefined" && content !== false);
    }

    return {
        getProxyMeta() {
            return getContent("meta[name=pv-proxy-endpoint]");
        },
        getLocalMeta() {
            return getContent("meta[name=pv-cache-path]");
        },
        hasProxyMeta() {
            return hasContent("meta[name=pv-proxy-endpoint]");
        },
        hasLocalMeta() {
            return hasContent("meta[name=pv-cache-path]");
        }
    };

}());

const PvStorage = (function () {
    const Keys = {
        KEY_PV: "pv",
        KEY_PV_SRC: "pv_src",
        KEY_CREATION: "pv_created_date"
    };

    const Source = {
        LOCAL: "same-origin",
        PROXY: "cors"
    };

    function get(key) {
        return localStorage.getItem(key);
    }

    function set(key, val) {
        localStorage.setItem(key, val);
    }

    function saveCache(pv, src) {
        set(Keys.KEY_PV, pv);
        set(Keys.KEY_PV_SRC, src);
        set(Keys.KEY_CREATION, new Date().toJSON());
    }

    return {
        keysCount() {
            return Object.keys(Keys).length;
        },
        hasCache() {
            return (localStorage.getItem(Keys.KEY_PV) !== null);
        },
        getCache() {
            return JSON.parse(localStorage.getItem(Keys.KEY_PV));
        },
        saveLocalCache(pv) {
            saveCache(pv, Source.LOCAL);
        },
        saveProxyCache(pv) {
            saveCache(pv, Source.PROXY);
        },
        isExpired() {
            let date = new Date(get(Keys.KEY_CREATION));
            date.setHours(date.getHours() + 1);   // per hour
            return Date.now() >= date.getTime();
        },
        isFromLocal() {
            return get(Keys.KEY_PV_SRC) === Source.LOCAL;
        },
        isFromProxy() {
            return get(Keys.KEY_PV_SRC) === Source.PROXY;
        },
        newerThan(pv) {
            return PvStorage.getCache().totalsForAllResults["ga:pageviews"] > pv.totalsForAllResults["ga:pageviews"];
        },
        inspectKeys() {
            if (localStorage.length !== PvStorage.keysCount()) {
                localStorage.clear();
                return;
            }

            for (let i = 0; i < localStorage.length; i++) {
                const key = localStorage.key(i);
                switch (key) {
                    case Keys.KEY_PV:
                    case Keys.KEY_PV_SRC:
                    case Keys.KEY_CREATION:
                        break;
                    default:
                        localStorage.clear();
                        return;
                }
            }
        }
    };
}()); /* PvStorage */

function countUp(min, max, destId) {
    if (min < max) {
        let numAnim = new CountUp(destId, min, max);
        if (!numAnim.error) {
            numAnim.start();
        } else {
            console.error(numAnim.error);
        }
    }
}

function countPV(path, rows) {
    let count = 0;

    if (typeof rows !== "undefined") {
        for (let i = 0; i < rows.length; ++i) {
            const gaPath = rows[parseInt(i, 10)][0];
            if (gaPath === path) { /* path format see: site.permalink */
                count += parseInt(rows[parseInt(i, 10)][1], 10);
                break;
            }
        }
    }

    return count;
}

function tacklePV(rows, path, elem, hasInit) {
    let count = countPV(path, rows);
    count = (count === 0 ? 1 : count);

    if (!hasInit) {
        elem.text(new Intl.NumberFormat().format(count));
    } else {
        const initCount = parseInt(elem.text().replace(/,/g, ""), 10);
        if (count > initCount) {
            countUp(initCount, count, elem.attr("id"));
        }
    }
}

function displayPageviews(data) {
    if (typeof data === "undefined") {
        return;
    }

    let hasInit = getInitStatus();
    const rows = data.rows; /* could be undefined */

    if ($("#post-list").length > 0) { /* the Home page */
        $(".post-preview").each(function () {
            const path = $(this).find("a").attr("href");
            tacklePV(rows, path, $(this).find(".pageviews"), hasInit);
        });

    } else if ($(".post").length > 0) { /* the post */
        const path = window.location.pathname;
        tacklePV(rows, path, $("#pv"), hasInit);
    }
}

function fetchProxyPageviews() {
    if (PvOpts.hasProxyMeta()) {
        $.ajax({
            type: "GET",
            url: PvOpts.getProxyMeta(),
            dataType: "jsonp",
            jsonpCallback: "displayPageviews",
            success: (data) => {
                PvStorage.saveProxyCache(JSON.stringify(data));
            },
            error: (jqXHR, textStatus, errorThrown) => {
                console.log("Failed to load pageviews from proxy server: " + errorThrown);
            }
        });
    }
}

function fetchLocalPageviews(hasCache = false) {
    return fetch(PvOpts.getLocalMeta())
        .then(response => response.json())
        .then(data => {
            if (hasCache) {
                // The cache from the proxy will sometimes be more recent than the local one
                if (PvStorage.isFromProxy() && PvStorage.newerThan(data)) {
                    return;
                }
            }
            displayPageviews(data);
            PvStorage.saveLocalCache(JSON.stringify(data));
        });
}

$(function () {
    if ($(".pageviews").length <= 0) {
        return;
    }

    PvStorage.inspectKeys();

    if (PvStorage.hasCache()) {
        displayPageviews(PvStorage.getCache());

        if (PvStorage.isExpired()) {
            if (PvOpts.hasLocalMeta()) {
                fetchLocalPageviews(true).then(fetchProxyPageviews);
            } else {
                fetchProxyPageviews();
            }

        } else {
            if (PvStorage.isFromLocal()) {
                fetchProxyPageviews();
            }
        }

    } else { // no cached

        if (PvOpts.hasLocalMeta()) {
            fetchLocalPageviews().then(fetchProxyPageviews);
        } else {
            fetchProxyPageviews();
        }
    }

});
